﻿#include <windows.h>
#include <tchar.h>
#include <cstdint>
#include <winuser.h>
#include "MemLeak.h"

/*#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") */

#ifdef _DEBUG

HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
#define IMMERSIVETIMER 19
int nWidth = 300, nHeight = 300;

#define IDC_STATIC 10
#define IDC_BUTTON 11
#define IDC_BUTTON2 12
#define IDC_BUTTON3 13
#define IDC_CHECKBOX 14

static ATOM g_atom = NULL;
static HWND g_hwnd = NULL;
static HWND g_showStateStatic = NULL;

static wchar_t g_szEventName1[50] = { 0 };
static wchar_t g_szEventName2[50] = { 0 };
static wchar_t g_szEventName3[50] = { 0 };

void ShowMemleakOperateDlg()
{
    wsprintf(g_szEventName1, L"BtnEvent1%d", GetCurrentProcessId());
    wsprintf(g_szEventName2, L"BtnEvent2%d", GetCurrentProcessId());
    wsprintf(g_szEventName3, L"BtnEvent3%d", GetCurrentProcessId());

    DWORD WINAPI CreateMainDlg(LPVOID param);
    DWORD WINAPI WaitEventThread(LPVOID);
    auto hThread = CreateThread(NULL, 0, CreateMainDlg, 0, 0, NULL);
    if (hThread)
    {
        ::CloseHandle(hThread);
        hThread = INVALID_HANDLE_VALUE;
    }

    hThread = CreateThread(NULL, 0, WaitEventThread, 0, 0, NULL);
    if (hThread)
    {
        ::CloseHandle(hThread);
        hThread = INVALID_HANDLE_VALUE;
    }
}

const std::wstring GetCurrentExeName()
{
    wchar_t szPath[MAX_PATH] = { 0 };
    ::GetModuleFileName(0, szPath, _countof(szPath) - 1);
    std::wstring strName = szPath;
    size_t pos = strName.rfind(L'\\');
    if (pos != std::wstring::npos)
        strName = strName.substr(pos + 1, strName.length());

    return strName;
}

DWORD WINAPI WaitEventThread(LPVOID)
{
    HANDLE h1 = OpenEvent(EVENT_MODIFY_STATE, FALSE, g_szEventName1);;
    if(!h1)
        h1 = CreateEventW(NULL, FALSE, FALSE, g_szEventName1);
    HANDLE h2 = OpenEvent(EVENT_MODIFY_STATE, FALSE, g_szEventName2);
    if (!h2)
        h2 = CreateEvent(NULL, FALSE, FALSE, g_szEventName2);
    HANDLE h3 = OpenEvent(EVENT_MODIFY_STATE, FALSE, g_szEventName3);
    if(!h3)
        h3 = CreateEvent(NULL, FALSE, FALSE, g_szEventName3);

    wchar_t szEvent[30] = { 0 };
    wsprintf(szEvent, L"ExitEvent%d", GetCurrentProcessId());
    HANDLE h4 = CreateEvent(NULL, FALSE, FALSE, szEvent);
    HANDLE h[4] = { h1, h2, h3, h4};
    while (1)
    {
        DWORD dwRet = WaitForMultipleObjects(4, h, FALSE, INFINITE);
        if (0 == dwRet)
        {
            CMemLeak::Instance().dumpCurrentMemoryTrace(500);
            extern TCHAR	g_OutputfileName[MAX_PATH];
            wchar_t szBuf[300] = { 0 };
            wsprintf(szBuf, L"日志已经输出到:%s", g_OutputfileName);
            //SetWindowTextW(g_showStateStatic, szBuf);
            SendMessageTimeoutW(g_showStateStatic, WM_SETTEXT, 0, (LPARAM)szBuf, SMTO_NORMAL, 100, NULL);
            //SendMessage(g_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON, BN_CLICKED), 0);
        }
        if (1 == dwRet)
        {
            static int iNum = 50;
            static int iTotalNum = 0;
            iTotalNum += iNum;
            new char[iNum];
            wchar_t szBuf[100] = { 0 };
            wsprintf(szBuf, L"已经开辟内存:%d字节, 测试累计开辟:%d字节。", iNum, iTotalNum);
            SendMessageTimeoutW(g_showStateStatic, WM_SETTEXT, 0, (LPARAM)szBuf, SMTO_NORMAL, 100, NULL);
            iNum += 35;
            //SendMessage(g_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON2, BN_CLICKED), 0);
        }
        if (2 == dwRet)
        {
            CMemLeak::Instance().ClearMemStatistics();
            //SetWindowTextW(g_showStateStatic, L"已经清空之前统计开辟的内存信息。");
            SendMessageTimeoutW(g_showStateStatic, WM_SETTEXT, 0, (LPARAM)L"已经清空之前统计开辟的内存信息。", SMTO_NORMAL, 100, NULL);
            //SendMessage(g_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON3, BN_CLICKED), 0);
        }
        if (3 == dwRet)
        {
            break;
        }
    }
    return 0;
}

void CloseMemleakOperateDlg()
{
    wchar_t szEvent[30] = { 0 };
    wsprintf(szEvent, L"ExitEvent%d", GetCurrentProcessId());
    HANDLE h = OpenEvent(EVENT_MODIFY_STATE, FALSE, szEvent);
    SetEvent(h);
    if (g_hwnd)
    {
        ::SendMessage(g_hwnd, WM_DESTROY, 0, 0);
        Sleep(100);
    }
}

DWORD WINAPI CreateMainDlg(LPVOID param)
{
    hInst = GetModuleHandle(NULL);
    WNDCLASSEX wcex =
    {
    sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
    LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("WindowClass"), NULL,
    };
    ATOM atom = RegisterClassEx(&wcex);
    g_atom = atom;
    if (!atom)
        return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
    int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;

    wchar_t szBufTitle[50] = { 0 };
    wsprintf(szBufTitle, L"%s*-MemDet", GetCurrentExeName().substr(0, 7).c_str());
    HWND hWnd = CreateWindowExW(0, (LPCWSTR)atom/*wcex.lpszClassName*/, (szBufTitle), (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU /* | WS_THICKFRAME */ | WS_MINIMIZEBOX),
        nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
    if (!hWnd)
        return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
    g_hwnd = hWnd;
    ::SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);

    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND hWndButton = NULL, hWndButton2 = NULL, hWndButton3 = NULL, hWndChkBox = NULL, hWndStatic = NULL;
    int wmId, wmEvent;

    switch (message)
    {
    case WM_CREATE:
    {
        static bool bFirst = true;
        if (bFirst)
        {
            bFirst = false;
            hWndButton = CreateWindowExW(0, L"Button", L"打印当前开辟内存", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE, 85, 120, 130, 32, hWnd, (HMENU)IDC_BUTTON, hInst, NULL);
            hWndButton2 = CreateWindowEx(0, L"Button", L"开辟内存(测试)", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE, 85, 120 + 40, 130, 32, hWnd, (HMENU)IDC_BUTTON2, hInst, NULL);
            hWndButton3 = CreateWindowEx(0, L"Button", L"清空开辟内存统计", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE, 85, 120 + 40 + 40, 130, 32, hWnd, (HMENU)IDC_BUTTON3, hInst, NULL);
            //hWndChkBox = CreateWindowEx(0, L"Button", L"控制其他窗口", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 5, 120 + 40 + 40 + 45, 110, 32, hWnd, (HMENU)IDC_CHECKBOX, hInst, NULL);

            g_showStateStatic = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), TEXT("请将pdb文件放到程序目录下，正常退出程序，会输出泄漏内存堆栈信息到:\"mlcurrentdetector-*.log\"。"),
                WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_VSCROLL | ES_WANTRETURN,
                0, 0, nWidth - 20, 90, hWnd, (HMENU)(IDC_BUTTON2 + 3), hInst, 0);
            return 0;
        }
    }
    break;
    case WM_COMMAND:
    {
        wmId = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDC_BUTTON:
        {
            HANDLE h1 = OpenEvent(EVENT_MODIFY_STATE, FALSE, g_szEventName1);
            SetEvent(h1);
        }
        break;
        case IDC_BUTTON2:
        {
            HANDLE h2 = OpenEvent(EVENT_MODIFY_STATE, FALSE, g_szEventName2);
            SetEvent(h2);
        }
        break;
        case IDC_BUTTON3:
        {
            HANDLE h3 = OpenEvent(EVENT_MODIFY_STATE, FALSE, g_szEventName3);
            SetEvent(h3);
        }
        break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hDC = BeginPaint(hWnd, &ps);

        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
    {
       PostQuitMessage(0);
    }
    break;
    case WM_TIMER:
    {
    }
    break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

#endif _DEBUG